/**********************************************
//SD reader
//ee_hqxaa, hilda
// with reference to Zhang Qi Bo's MMC_SD.c under GNU
*********************************************/
// http://209.85.175.104/search?q=cache:z0LhiLyBT4sJ:www.embeddedrelated.com/groups/lpc2000/show/18527.php+CMD9+capacity+avr&hl=zh-TW&ct=clnk&cd=1&gl=hk
// http://elm-chan.org/docs/mmc/mmc_e.html
// with reference to Sandisk SD MMC datasheet, we find the SPI communication protocal
// http://www.mcuol.com/download/upfile/20070914090725_SD%e7%bb%9d%e5%af%86%e8%b5%84%e6%96%99.pdf
// CMD0 : GO_IDLE_STATE			//reset SD card
// CMD1 : SEND_OP_COND			//activate SD initialization process
// CMD9 : SEND_CSD				//asd SD to send its card specific data
// CMD16: SET_BLOCKLEN			//select a block size for all following commands, arg32bit
// CMD17: READ_SINGLE_BLOCK		//read one block, arg32bit
// CMD24: WRITE_BLOCK			//write one block, arg32bit
// CMD59: CRC_ON_OFF			//arg32bit, 1/0 to turn on/off CRC
//*********************************************/

//with reference to 2006 NXP Semiconductor//
#define F_CPU 7372800UL

#include <avr/io.h>
#include <util/delay.h>
#include "SD.h"


//**************************
//****config general SPI****
//**************************

void SPI_Low(void){
	// enable SP, Master, frequency = fosc/64
	// for 128 at 16M, will be 250kHz
	SPCR = _BV(SPE)|_BV(MSTR)|_BV(SPR1)|_BV(SPR0);
	// disable double SCK speed
	SPSR &= ~_BV(SPI2X);
}

void SPI_High(void){
	// freq = fosc/4
	SPCR = _BV(SPE)|_BV(MSTR);
	// enable double SCK speed, in MSTR, fosc = CLK/2
	SPSR |=_BV(SPI2X);
}


//port initialization
void SPI_Init(void){
	DDR_INI();					// macro, config SS MOSI and SCK for output to SD
	SPI_Low();					// try slow mode
}


// m128 write one byte to SPI
// if is to read one byte, write 0xff to generate clock, and wait for flag to set
// return received byte
uint8 SPI_WriteByte(uint8 val){
	SPDR = val;
	while(!(SPSR & _BV(SPIF)));  //wait for finish_transmit flag to be set
	return SPDR;
}









//**************************
//****config SD card****
//**************************

void SD_Init(void){
	SPI_Init();						// SPI in slow mode
	SPI_CS_Deassert();				// SS =1, active low
}



// send a command to SD
// and read one byte response
// command formate: [40 ** ** ** ** 95], in hex
uint8 SD_SendCommand(uint8 cmd, uint32 arg){
	uint8 r1;
	uint8 retry=0;

	SPI_WriteByte(0xff);			// send 8 clock pulse at asssert mode to synchronize SD card at deassert mode
	SPI_CS_Assert();				// issue a request

		SPI_WriteByte(cmd | 0x40);	// send command, set bit 01000000 to high
		SPI_WriteByte(arg>>24);		// first 8 bit
		SPI_WriteByte(arg>>16);		// second 8 bit
		SPI_WriteByte(arg>>8);		// third 8 bit
		SPI_WriteByte(arg);			// lasat 8 bit

		if(cmd == 0)
			SPI_WriteByte(0x95);		// write 10010101 CRC for RESET command only
		if(cmd == 8)
			SPI_WriteByte(0x87);
		else SPI_WriteByte(0);


		if(cmd == 12)SPI_WriteByte(0xff);	//skip one byte when stop read
		//wait for response, if error time more than 20, give up
		while((r1 = SPI_WriteByte(0xff))& 0x80)
			if(retry++ >20)break;

	SPI_CS_Deassert();				// finish one request

	return r1;
	// if r1 = 0xff, send command faild, else hv got some response
}






uint8 SD_Reset(void){
	uint8 i;
	uint8 retry = 0;
	uint8 r1 = 0;
	SPI_Low();
	for(i = 0; i<13; i++){_delay_ms(20);}

	//RESET***************************************************************
	do{
		for(i=0;i<10;i++) SPI_WriteByte(0xff);		//80 pulse to synchronize
		r1 = SD_SendCommand(0,0);					// send CMD0
		retry++;
		if(retry>100) return 1;						// r1 not 0x01, time out fail to reset
	}while(r1!=0x01);





	// MMC or SD activate**************************************************
	retry = 0;

	if(SD_SendCommand(8,0x1AA)==1){
		// write cmd55 and cmd41 to SD card
		for(i = 0; i<4; i++) r1 = SPI_WriteByte(0xff);
		do{
			retry++;
			if(retry > 10) return 1;
		}while(!(SD_SendCommand(55,0)<=1 && SD_SendCommand(41, 1UL<<30)==0));

	}else{
		// write cmd 1 if it is a mmc card
		i = 0;
		if(SD_SendCommand(55,0)<=1 && SD_SendCommand(41,0)<=1) i = 4;
		do{
			retry++;
			if(retry>100) return 1;
		}while(!  ( (!i && SD_SendCommand(1,0)==0) || (i && SD_SendCommand(55,0)<=1 && SD_SendCommand(41,0)==0) )  );
	}




	// after initialize, set working frequency to HIGH*********************
	SPI_High();

	r1 = SD_SendCommand(59,0);		// send  CMD59, disale CRC
	r1 = SD_SendCommand(16, 512);	// set sector size to 512 byte
	SPI_WriteByte(0xff);
	return 0;						// reset successful retrun 0
}











//read a sector
uint8 SD_ReadSingleBlock(uint32 sector, uint8* buffer){
	uint8 r1;
	uint16 i;
	uint16 retry = 0;


	r1 = SD_SendCommand(17, sector<<9); //send read command, address is the sector number * 512
	if(r1 != 0x00) {


	PORTA |= _BV(4);
		//=====================================
		// this is for debugging...remove later
		//=====================================
		/*while(1){
			unsigned char count;
			PORTA |= _BV(5);
			for(count=0; count<20; count++)_delay_ms(30);
			PORTA &=~_BV(5);
			for(count=0; count<20; count++)_delay_ms(30);
		}*/
		return r1;			//some error, failed to issue read command
	}

/*	unsigned char count;
	for(count=0; count<20; count++)_delay_ms(30);
*/

	_delay_us(60);						// this delay is necessary, without it , will return 1 of readsingleblock
										// and if less than 60, for high address , cannot read



	SPI_CS_Assert();					//SS = 0

		while(SPI_WriteByte(0xff)!=0xfe)if(retry++>1000){
				SPI_CS_Deassert();
				PORTA |= _BV(3);

				//=====================================
				// this is for debugging. remove later
				//=====================================

				//unsigned char count;
				/*PORTA |= _BV(6);
				for(count=0; count<20; count++)_delay_ms(30);
				PORTA &=~_BV(6);
				for(count=0; count<20; count++)_delay_ms(30);

				while(1){
					//unsigned char count;
					PORTA |= _BV(6);
					for(count=0; count<20; count++)_delay_ms(30);
					PORTA &=~_BV(6);
					for(count=0; count<20; count++)_delay_ms(30);
				}*/
				return 1;					//timeout failed to receive 0xfe
		}

		for(i=0; i<512; i++){
			*buffer++ = SPI_WriteByte(0xff);
		}

		SPI_WriteByte(0xff);				//CRC byte
		SPI_WriteByte(0xff);				//CRC byte


	SPI_CS_Deassert();					//finish one block read to buffer

	return 0;
}









//*******************************************
//how to read SD_card capacity
//fomula from SANDISK SD datasheet
//BLOCKNR = (C_SIZE+1) * MULT
//MULT = 2^(C_SIZE_MULT+2)    (C_SIZE_MULT < 8)
//BLOCK_LEN = 2^READ_BL_LEN   (READ_BL_LEN < 12)
//*******************************************

uint32 SD_ReadCapacity(void){
	uint8 r1;
	uint16 i;
	uint16 temp;
	uint8 buffer[16];
	uint32 Capacity;

	r1 = SD_SendCommand(9,0);			//ask for CSD
	if(r1!=0x00)return r1;				// receive R1

	// if r1 zero
	SPI_CS_Assert();
		while(SPI_WriteByte(0xff)!=0xfe);	//wait for valid_data response start

		for(i=0; i<16; i++){
			buffer[i] = SPI_WriteByte(0xff);
		}

		SPI_WriteByte(0xff);			//16bit CRC follows data
		SPI_WriteByte(0xff);

		SPI_WriteByte(0xff);			//???????

	SPI_CS_Deassert();					//finish CSD data read

	//--------------------------------C_SIZE [73:62],and we ignored the first byte
	i = buffer[6]&0x03;					//read byte 7 bit 1..0
	i<<=8;								//read byte 8
	i += buffer[7];						//read byte 9 bit 7..6
	i<<=2;								//concatenated
	i += ((buffer[8]&0xc0)>>6);
	//-------------------------------C_SIZE_MULT [49:47]
	r1 = buffer[9]&0x03;				//read byte10 bit 1..0
	r1<<=1;
	r1 += (buffer[10]&0x80>>7);			//read byte11 bit 7
	r1 += 2;							//C_SIZE_MULT + 2
	temp = 1;
	/*while(r1){temp*=2; r1--;}*/
	temp <<=(r1);
	//-------------------------------BLOCK_NR
	Capacity = ((uint32)(i+1)*(uint32)temp);	//number of blocks
	//-------------------------------READ_BL_LEN
	i = buffer[5]&0x0f;					//read byte 6 bit 3..0
	temp = 1;
	temp<<=(i);						//block length
	//-------------------------------final capacity
	Capacity *= (uint32)temp;
	return Capacity;

}
